/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtMultiTable.h"
#include "WtTableCmds.h"
#include "WtContext.h"
#include "WtUtil.h"
#include "WtTable.h"
#include "WtTableUtil.h"

/* Create a custom command that operates on a table */

Tcl_Command WtCreateMultiTableCmd(const char *cmdName,
  Tcl_Obj **tblPtr, Tcl_ObjType *type, Tcl_Interp *interp)
{
  WtTableCmdData *tcd = (WtTableCmdData *)ckalloc(sizeof(WtTableCmdData));
  tcd->tablePtr = tblPtr;
  tcd->type = type;
  return Tcl_CreateObjCommand(interp, cmdName, WtNamedMultiTableCmd,
    (ClientData)tcd, WtDeleteMultiTableCmdProc);
}

void WtDeleteMultiTableCmdProc(ClientData clientData)
{
  if (clientData) {
    ckfree(clientData);
  }
}

/* The custom table command procedure */

int WtNamedMultiTableCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtTableCmdData *tcd = (WtTableCmdData *)clientData;
  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    tcd->tablePtr, tcd->type, 0, 1);
  return ret;
}

WtTableCmdStatus WtMultiTableMethodHelper(ClientData clientData,
    Tcl_Interp *interp, int objc, Tcl_Obj *const objv[],
    int *tclRet, Tcl_Obj **tblPtr, Tcl_ObjType *type, int readOnly,
    int appendUsage)
{
  int status = WT_TBL_CMD_DONE, ret = TCL_ERROR, error = 0, listLen;
  WtContext *w = WtGetAssocContext(interp);
  char *subCmd;
  Tcl_Obj *tbl, *obj, *list;

  if (objc < 2) {
    if (appendUsage) {
      WtMultiTableMethodUsageError(interp, objv[0], readOnly);
    }

    *tclRet = TCL_ERROR;

    return WT_TBL_CMD_MISSING;
  }

  subCmd = WtToString(objv[1]);
  tbl = *tblPtr;

  if (!strcmp(subCmd, "count")) {

    /* count command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, Tcl_NewIntObj(WtTableSize(tbl)));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "has")) {

    /* has command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " key", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      obj = WtTableGet(tbl, objv[2]);
      Tcl_SetObjResult(interp, Tcl_NewBooleanObj(obj != NULL));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "keys")) {

    /* keys command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp) &&
        WtTableKeys(tbl, &list, interp)) {
      Tcl_SetObjResult(interp, list);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "values")) {

    /* values command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp) &&
        WtTableFirstValues(tbl, &list, interp)) {
      Tcl_SetObjResult(interp, list);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "allValues")) {

    /* allValues command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp) &&
        WtTableValues(tbl, &list, interp)) {
      Tcl_SetObjResult(interp, list);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "get")) {

    /* get command */

    if (objc < 3 || objc > 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ",
        WtToString(objv[1]), " key ?defaultValue?", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp) &&
        WtTableGetFirst(tbl, objv[2], &obj, interp)) {
      if (obj) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      } else if (objc > 3) {
        Tcl_SetObjResult(interp, objv[3]);
        ret = TCL_OK;
      } else {
        Tcl_AppendResult(interp, "No entry with key \"",
          WtToString(objv[2]), "\" found.", NULL);
      }
    }

  } else if (!strcmp(subCmd, "getAll")) {

    /* getAll command */

    if (objc != 3 && objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key ?defaultValue?", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      list = WtTableGet(tbl, objv[2]);
      if (list) {
        Tcl_SetObjResult(interp, list);
        ret = TCL_OK;
      } else if (objc > 3) {
        Tcl_SetObjResult(interp, objv[3]);
        ret = TCL_OK;
      } else {
        Tcl_AppendResult(interp, "No entry with key \"",
          WtToString(objv[2]), "\" found.", NULL);
      }
    }

  } else if (!strcmp(subCmd, "set") && !readOnly) {

    /* set command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key value", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtTableSetOneValueList(tbl, objv[2], objv[3], &obj);
      Tcl_SetObjResult(interp, obj);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "setDefault") && !readOnly) {

    /* setDefault command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ",
        " key value", NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (WtTableGetFirst(tbl, objv[2], &obj, interp)) {
        if (obj) {
          Tcl_SetObjResult(interp, obj);
          ret = TCL_OK;
        } else if (WtOwnTableObj(tblPtr, type, interp)) {
          tbl = *tblPtr;
          WtTableSetOneValueList(tbl, objv[2], objv[3], &obj);
          Tcl_SetObjResult(interp, obj);
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "setAll") && !readOnly) {

    /* setAll command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[3], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value must be a list. Usage: ", WtToString(objv[0]),
          " ", WtToString(objv[1]), " key list", NULL);
      } else if (WtOwnTableObj(tblPtr, type, interp)) {
        WtTableSet(tbl, objv[2], objv[3]);
        Tcl_SetObjResult(interp, objv[3]);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "setAllDefault") && !readOnly) {

    /* setAllDefault command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[3], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value must be a list. Usage: ",
          WtToString(objv[0]), " ", WtToString(objv[1]),
          " key list", NULL);
      } else if (WtConvertToTableObj(tbl, type, interp)) {
        if (WtTableGetList(tbl, objv[2], &list, interp)) {
          if (list) {
            Tcl_SetObjResult(interp, list);
          } else if (WtOwnTableObj(tblPtr, type, interp)) {
            tbl = *tblPtr;
            WtTableSet(tbl, objv[2], objv[3]);
            Tcl_SetObjResult(interp, objv[3]);
          }
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "add") && !readOnly) {

    /* add command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key value", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      if (WtTableAppendToList(tbl, objv[2], objv[3], &list, interp)) {
        Tcl_SetObjResult(interp, list);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "addList") && !readOnly) {

    /* addList command */

    if (objc != 4) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " key list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[3], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value is not a list. Usage: ",
          WtToString(objv[0]), " ", WtToString(objv[1]),
          " key list", NULL);
      } else if (WtOwnTableObj(tblPtr, type, interp)) {
        tbl = *tblPtr;
        if (WtTableAppendListToList(tbl, objv[2], objv[3], &list, interp)) {
          Tcl_SetObjResult(interp, list);
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "remove") && !readOnly) {

    /* remove command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " key", NULL);
    } else if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtTableRemove(tbl, objv[2]);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "removeList") && !readOnly) {

    /* removeList command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " list", NULL);
    } else {
      if (Tcl_ListObjLength(interp, objv[2], &listLen) != TCL_OK) {
        Tcl_AppendResult(interp,
          "Value must be a list. Usage: ",
          WtToString(objv[0]), " ", WtToString(objv[1]), " list", NULL);
      } else if (WtOwnTableObj(tblPtr, type, interp)) {
        tbl = *tblPtr;
        if (WtTableRemoveList(tbl, objv[2], interp)) {
          ret = TCL_OK;
        }
      }
    }

  } else if (!strcmp(subCmd, "items")) {

    /* items command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, tbl);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "list")) {

    /* list command */

    if (objc != 2) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (obj = WtTableToList(tbl, 0, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "array")) {

    /* array command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " arrayName",
        NULL);
    } else if (WtConvertToTableObj(tbl, type, interp)) {
      if (WtTableToArray(interp, tbl, objv[2], 0)) {
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "copy")) {

    /* copy command */

    if (WtConvertToTableObj(tbl, type, interp)) {
      Tcl_SetObjResult(interp, Tcl_DuplicateObj(tbl));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "clear") && !readOnly) {

    /* clear command */

    if (WtOwnTableObj(tblPtr, type, interp)) {
      tbl = *tblPtr;
      WtClearTable(tbl);
      ret = TCL_OK;
    }

  } else {

    /* unknown command */

    status = WT_TBL_CMD_NOT_FOUND;

    if (appendUsage) {
      WtMultiTableMethodUsageError(interp, objv[0], readOnly);
    }

  }

  *tclRet = ret;

  return status;
}

/* Multi table usage error */

void WtMultiTableMethodUsageError(Tcl_Interp *interp, Tcl_Obj *cmd,
    int readOnly)
{
  Tcl_AppendResult(interp, "Usage:\n\n", NULL);
  WtTableMethodAppendUsage(interp, cmd, readOnly);
}

/* Multi table usage */

void WtMultiTableMethodAppendUsage(Tcl_Interp *interp, Tcl_Obj *cmd, int readOnly)
{
  Tcl_Obj *res = Tcl_GetObjResult(interp);
  char *cmdStr = WtToString(cmd);

  if (readOnly) {
    Tcl_AppendStringsToObj(res,
      cmdStr, " count\n",
      cmdStr, " has key\n",
      cmdStr, " get key ?defaultValue?\n",
      cmdStr, " getAll key ?defaultValue?\n",
      cmdStr, " keys\n",
      cmdStr, " values\n",
      cmdStr, " allValues\n",
      cmdStr, " items\n",
      cmdStr, " list\n",
      cmdStr, " array arrayName\n",
      cmdStr, " copy\n",
      NULL);
  } else {
    Tcl_AppendStringsToObj(res,
      cmdStr, " count\n",
      cmdStr, " has key\n",
      cmdStr, " keys\n",
      cmdStr, " values\n",
      cmdStr, " allValues\n",
      cmdStr, " get key ?defaultValue?\n",
      cmdStr, " getAll key ?defaultValue?\n",
      cmdStr, " set key value\n",
      cmdStr, " setDefault key value\n",
      cmdStr, " setAll key list\n",
      cmdStr, " setAllDefault key list\n",
      cmdStr, " add key value\n",
      cmdStr, " addList key list\n",
      cmdStr, " remove key\n",
      cmdStr, " removeList keys\n",
      cmdStr, " items\n",
      cmdStr, " list\n",
      cmdStr, " array arrayName\n",
      cmdStr, " copy\n",
      cmdStr, " clear\n",
      NULL);
  }
}
